@strapi/admin 4.0.4 → 4.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/admin/src/components/UpgradePlanModal/index.js +7 -2
  2. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/isValidJSONString.js +15 -0
  3. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/schema.js +11 -12
  4. package/admin/src/content-manager/components/InputJSON/FieldWrapper.js +32 -0
  5. package/admin/src/content-manager/components/InputJSON/Label.js +5 -11
  6. package/admin/src/content-manager/components/InputJSON/index.js +27 -27
  7. package/admin/src/content-manager/components/PreviewWysiwyg/Wrapper.js +4 -0
  8. package/admin/src/content-manager/components/PreviewWysiwyg/utils/mdRenderer.js +1 -1
  9. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +4 -0
  10. package/admin/src/content-manager/components/Wysiwyg/Editor.js +4 -1
  11. package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +2 -1
  12. package/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js +14 -8
  13. package/admin/src/content-manager/components/Wysiwyg/WysiwygStyles.js +1 -0
  14. package/admin/src/content-manager/components/Wysiwyg/index.js +4 -1
  15. package/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js +1 -3
  16. package/admin/src/content-manager/pages/EditSettingsView/components/FormModal.js +4 -3
  17. package/admin/src/content-manager/pages/EditSettingsView/components/ModalForm.js +52 -11
  18. package/admin/src/content-manager/pages/EditSettingsView/index.js +10 -1
  19. package/admin/src/content-manager/pages/EditSettingsView/reducer.js +26 -5
  20. package/admin/src/content-manager/pages/EditSettingsView/utils/layout.js +40 -9
  21. package/admin/src/content-manager/pages/EditView/index.js +2 -1
  22. package/admin/src/pages/Admin/index.js +2 -0
  23. package/admin/src/pages/InternalErrorPage/index.js +53 -0
  24. package/admin/src/pages/MarketplacePage/index.js +13 -13
  25. package/admin/src/pages/NotFoundPage/index.js +45 -2
  26. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +1 -1
  27. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/index.js +2 -10
  28. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/utils/schema.js +1 -4
  29. package/admin/src/pages/SettingsPage/pages/Webhooks/ListView/index.js +4 -1
  30. package/admin/src/translations/dk.json +300 -110
  31. package/admin/src/translations/en.json +5 -1
  32. package/admin/src/translations/es.json +204 -4
  33. package/admin/src/translations/fr.json +3 -0
  34. package/admin/src/translations/hu.json +672 -0
  35. package/admin/src/translations/ja.json +327 -7
  36. package/admin/src/translations/languageNativeNames.js +1 -0
  37. package/admin/src/translations/nl.json +512 -125
  38. package/admin/src/translations/pt-BR.json +398 -19
  39. package/admin/src/translations/uk.json +40 -40
  40. package/admin/src/translations/zh-Hans.json +9 -9
  41. package/admin/src/translations/zh.json +7 -0
  42. package/build/1856.a06395b4.chunk.js +1 -0
  43. package/build/2481.7d15bd79.chunk.js +1 -0
  44. package/build/2912.38fb9bd1.chunk.js +1 -0
  45. package/build/{6354.48168bc8.chunk.js → 3215.4d042146.chunk.js} +2 -2
  46. package/build/{6354.48168bc8.chunk.js.LICENSE.txt → 3215.4d042146.chunk.js.LICENSE.txt} +0 -0
  47. package/build/4261.a4e1e93c.chunk.js +1 -0
  48. package/build/4362.e3d2d72b.chunk.js +1 -0
  49. package/build/460.639962f0.chunk.js +2 -0
  50. package/build/{6060.3af8877e.chunk.js.LICENSE.txt → 460.639962f0.chunk.js.LICENSE.txt} +0 -0
  51. package/build/4715.31ca1967.chunk.js +1 -0
  52. package/build/4741.c2bfe032.chunk.js +2 -0
  53. package/build/{4741.1fb6ad6e.chunk.js.LICENSE.txt → 4741.c2bfe032.chunk.js.LICENSE.txt} +0 -0
  54. package/build/497.8f30da61.chunk.js +1 -0
  55. package/build/4982.da4adb38.chunk.js +1 -0
  56. package/build/5032.ed02a466.chunk.js +2 -0
  57. package/build/{8530.a978bde6.chunk.js.LICENSE.txt → 5032.ed02a466.chunk.js.LICENSE.txt} +0 -0
  58. package/build/6250.dc6d7a58.chunk.js +1 -0
  59. package/build/6925.bd694b04.chunk.js +2 -0
  60. package/build/{6925.4767e761.chunk.js.LICENSE.txt → 6925.bd694b04.chunk.js.LICENSE.txt} +2 -1
  61. package/build/7841.ef9bcee9.chunk.js +1 -0
  62. package/build/849.9075d399.chunk.js +1 -0
  63. package/build/9235.ced8aebf.chunk.js +1 -0
  64. package/build/9238.bdd93dae.chunk.js +1 -0
  65. package/build/Admin-authenticatedApp.f65c428a.chunk.js +1 -0
  66. package/build/Admin_homePage.a20b5e76.chunk.js +1 -0
  67. package/build/Admin_marketplace.e8654056.chunk.js +1 -0
  68. package/build/Admin_pluginsPage.7d1bd7ce.chunk.js +1 -0
  69. package/build/{Admin_profilePage.077e17a5.chunk.js → Admin_profilePage.67dd744c.chunk.js} +1 -1
  70. package/build/Admin_settingsPage.05877e0b.chunk.js +1 -0
  71. package/build/admin-edit-roles-page.2d1b6461.chunk.js +1 -0
  72. package/build/admin-edit-users.e736db15.chunk.js +1 -0
  73. package/build/admin-users.5f79c031.chunk.js +1 -0
  74. package/build/api-tokens-create-page.10586e16.chunk.js +1 -0
  75. package/build/{api-tokens-create-page.07be3e07.chunk.js → api-tokens-edit-page.f9e3038d.chunk.js} +1 -1
  76. package/build/{api-tokens-list-page.da9714d8.chunk.js → api-tokens-list-page.e071058b.chunk.js} +1 -1
  77. package/build/codemirror-css.48b438c9.chunk.js +1 -0
  78. package/build/{codemirror-javacript.381a518a.chunk.js → codemirror-javacript.8c7c015d.chunk.js} +1 -1
  79. package/build/codemirror-theme.b5559efc.chunk.js +1 -0
  80. package/build/content-manager.07db1dd9.chunk.js +1 -0
  81. package/build/{content-type-builder-translation-dk-json.5c6344f9.chunk.js → content-type-builder-translation-dk-json.098bd218.chunk.js} +1 -1
  82. package/build/content-type-builder-translation-es-json.20c177ee.chunk.js +1 -0
  83. package/build/content-type-builder.52f5975c.chunk.js +1 -0
  84. package/build/cropper-css.ace19575.chunk.js +1 -0
  85. package/build/dk-json.7356ea4b.chunk.js +1 -0
  86. package/build/email-settings-page.4338588d.chunk.js +1 -0
  87. package/build/email-translation-dk-json.f8a595bf.chunk.js +1 -0
  88. package/build/email-translation-es-json.eb303dea.chunk.js +1 -0
  89. package/build/en-json.3422a59e.chunk.js +1 -0
  90. package/build/es-json.ed9c8bef.chunk.js +1 -0
  91. package/build/fontawesome-css-all.3b89f909.chunk.js +1 -0
  92. package/build/fontawesome-css.36cff9ae.chunk.js +1 -0
  93. package/build/fr-json.390bcdeb.chunk.js +1 -0
  94. package/build/highlight.js.6321cb45.chunk.js +1 -0
  95. package/build/hu-json.a741d263.chunk.js +1 -0
  96. package/build/i18n-settings-page.51e37957.chunk.js +1 -0
  97. package/build/i18n-translation-dk-json.932d3cc2.chunk.js +1 -0
  98. package/build/i18n-translation-es-json.347904f3.chunk.js +1 -0
  99. package/build/index.html +1 -1
  100. package/build/ja-json.52581a2a.chunk.js +1 -0
  101. package/build/main.59b96514.js +2 -0
  102. package/build/{main.ca080a1e.js.LICENSE.txt → main.59b96514.js.LICENSE.txt} +0 -0
  103. package/build/nl-json.ac661b7f.chunk.js +1 -0
  104. package/build/pt-BR-json.8b3f799d.chunk.js +1 -0
  105. package/build/runtime~main.75c67df1.js +1 -0
  106. package/build/{sk-json.2eb1ec0d.chunk.js → sk-json.a40bc2c8.chunk.js} +1 -1
  107. package/build/sso-settings-page.c073b6d7.chunk.js +1 -0
  108. package/build/uk-json.da2ed14e.chunk.js +1 -0
  109. package/build/upload-settings.62631a39.chunk.js +1 -0
  110. package/build/{upload-translation-dk-json.e8c0a891.chunk.js → upload-translation-dk-json.bc6af8b4.chunk.js} +1 -1
  111. package/build/upload-translation-en-json.6b529046.chunk.js +1 -0
  112. package/build/upload-translation-es-json.b53d6641.chunk.js +1 -0
  113. package/build/{upload-translation-ja-json.568f097e.chunk.js → upload-translation-ja-json.71aa85eb.chunk.js} +1 -1
  114. package/build/upload.803ab265.chunk.js +1 -0
  115. package/build/users-advanced-settings-page.7694d3c9.chunk.js +1 -0
  116. package/build/users-email-settings-page.862eb51e.chunk.js +1 -0
  117. package/build/{users-permissions-translation-dk-json.a36b323b.chunk.js → users-permissions-translation-dk-json.3e0295e5.chunk.js} +1 -1
  118. package/build/users-permissions-translation-es-json.a4f81eaa.chunk.js +1 -0
  119. package/build/{users-permissions-translation-ru-json.44dd1f10.chunk.js → users-permissions-translation-ru-json.5709c5a0.chunk.js} +1 -1
  120. package/build/users-providers-settings-page.47f97b06.chunk.js +1 -0
  121. package/build/users-roles-settings-page.b67e2b4d.chunk.js +1 -0
  122. package/build/webhook-edit-page.adad0a42.chunk.js +1 -0
  123. package/build/webhook-list-page.5c8f2a91.chunk.js +1 -0
  124. package/build/zh-Hans-json.5843950b.chunk.js +1 -0
  125. package/build/zh-json.2e4c9ef4.chunk.js +1 -0
  126. package/ee/admin/pages/App/utils/customRoutes.js +1 -1
  127. package/ee/server/controllers/authentication/utils.js +1 -1
  128. package/index.js +43 -26
  129. package/package.json +13 -13
  130. package/server/bootstrap.js +16 -0
  131. package/server/controllers/webhooks.js +6 -10
  132. package/server/services/api-token.js +1 -1
  133. package/server/services/metrics.js +6 -0
  134. package/server/services/permission/permissions-manager/sanitize.js +13 -5
  135. package/server/services/user.js +10 -0
  136. package/server/strategies/api-token.js +0 -3
  137. package/build/1024.1b15fbb5.chunk.js +0 -1
  138. package/build/1856.0a6992d6.chunk.js +0 -1
  139. package/build/2912.d903b59a.chunk.js +0 -1
  140. package/build/3742.2281afd7.chunk.js +0 -1
  141. package/build/4045.0b43d55e.chunk.js +0 -1
  142. package/build/4064.2683bcce.chunk.js +0 -1
  143. package/build/4261.061aed35.chunk.js +0 -1
  144. package/build/4672.7ad6782a.chunk.js +0 -1
  145. package/build/4715.f134f37a.chunk.js +0 -1
  146. package/build/4741.1fb6ad6e.chunk.js +0 -2
  147. package/build/497.726adbfa.chunk.js +0 -1
  148. package/build/4982.a7f87e6c.chunk.js +0 -1
  149. package/build/6060.3af8877e.chunk.js +0 -2
  150. package/build/6250.7b4872b1.chunk.js +0 -1
  151. package/build/6925.4767e761.chunk.js +0 -2
  152. package/build/7841.d2aa6f5e.chunk.js +0 -1
  153. package/build/8530.a978bde6.chunk.js +0 -2
  154. package/build/Admin-authenticatedApp.8c375af0.chunk.js +0 -1
  155. package/build/Admin_homePage.f9cdd615.chunk.js +0 -1
  156. package/build/Admin_marketplace.4f6c77f2.chunk.js +0 -1
  157. package/build/Admin_pluginsPage.cd9a871c.chunk.js +0 -1
  158. package/build/Admin_settingsPage.9752ef85.chunk.js +0 -1
  159. package/build/admin-edit-roles-page.27b047fc.chunk.js +0 -1
  160. package/build/admin-edit-users.a0dede23.chunk.js +0 -1
  161. package/build/admin-users.a11177c1.chunk.js +0 -1
  162. package/build/api-tokens-edit-page.d6afc60e.chunk.js +0 -1
  163. package/build/codemirror-css.f9701755.chunk.js +0 -1
  164. package/build/codemirror-theme.1563c1c2.chunk.js +0 -1
  165. package/build/content-manager.51651b3b.chunk.js +0 -1
  166. package/build/content-type-builder-translation-es-json.7e2055ad.chunk.js +0 -1
  167. package/build/content-type-builder.c815d0c6.chunk.js +0 -1
  168. package/build/cropper-css.45c47fe3.chunk.js +0 -1
  169. package/build/dk-json.9516eb08.chunk.js +0 -1
  170. package/build/email-settings-page.03e18bb2.chunk.js +0 -1
  171. package/build/email-translation-dk-json.e1480892.chunk.js +0 -1
  172. package/build/email-translation-es-json.abbba8e3.chunk.js +0 -1
  173. package/build/en-json.9e3c8615.chunk.js +0 -1
  174. package/build/es-json.fa8ddd1d.chunk.js +0 -1
  175. package/build/fontawesome-css-all.9c41f1d7.chunk.js +0 -1
  176. package/build/fontawesome-css.24c8dbfc.chunk.js +0 -1
  177. package/build/fr-json.bae03a2c.chunk.js +0 -1
  178. package/build/highlight.js.90b600ee.chunk.js +0 -1
  179. package/build/i18n-settings-page.89d1776c.chunk.js +0 -1
  180. package/build/ja-json.57abaacb.chunk.js +0 -1
  181. package/build/main.ca080a1e.js +0 -2
  182. package/build/nl-json.d757328d.chunk.js +0 -1
  183. package/build/pt-BR-json.6d1cfdb2.chunk.js +0 -1
  184. package/build/runtime~main.6f530910.js +0 -1
  185. package/build/sso-settings-page.d4ab164b.chunk.js +0 -1
  186. package/build/uk-json.c50ad2a8.chunk.js +0 -1
  187. package/build/upload-settings.aa148b01.chunk.js +0 -1
  188. package/build/upload-translation-en-json.31ea0622.chunk.js +0 -1
  189. package/build/upload-translation-es-json.42b5c758.chunk.js +0 -1
  190. package/build/upload.1991e997.chunk.js +0 -1
  191. package/build/users-advanced-settings-page.23ed7ee9.chunk.js +0 -1
  192. package/build/users-email-settings-page.64dc429a.chunk.js +0 -1
  193. package/build/users-permissions-translation-es-json.c8ef51bd.chunk.js +0 -1
  194. package/build/users-providers-settings-page.329f102a.chunk.js +0 -1
  195. package/build/users-roles-settings-page.e28982b6.chunk.js +0 -1
  196. package/build/webhook-edit-page.678af4e2.chunk.js +0 -1
  197. package/build/webhook-list-page.5abda9c2.chunk.js +0 -1
  198. package/build/zh-Hans-json.fcc53388.chunk.js +0 -1
  199. package/build/zh-json.496bd6c4.chunk.js +0 -1
@@ -70,9 +70,14 @@ const UpgradePlanModal = ({ onClose, isOpen }) => {
70
70
 
71
71
  return (
72
72
  <Portal>
73
- <UpgradeWrapper>
73
+ <UpgradeWrapper onClick={onClose}>
74
74
  <FocusTrap onEscape={onClose}>
75
- <UpgradeContainer aria-labelledby="upgrade-plan" background="neutral0" hasRadius>
75
+ <UpgradeContainer
76
+ onClick={e => e.stopPropagation()}
77
+ aria-labelledby="upgrade-plan"
78
+ background="neutral0"
79
+ hasRadius
80
+ >
76
81
  <img src={AirBalloon} alt="air-balloon" />
77
82
  <CloseButtonContainer>
78
83
  <IconButton onClick={onClose} aria-label="Close" icon={<Cross />} />
@@ -0,0 +1,15 @@
1
+ const isValidJSONString = value => {
2
+ if (typeof value === 'string' && value.startsWith('"') && value.endsWith('"')) {
3
+ try {
4
+ JSON.parse(value);
5
+
6
+ return true;
7
+ } catch {
8
+ return false;
9
+ }
10
+ }
11
+
12
+ return false;
13
+ };
14
+
15
+ export default isValidJSONString;
@@ -1,18 +1,17 @@
1
- import {
2
- get,
3
- isBoolean,
4
- isNumber,
5
- isNull,
6
- isObject,
7
- isArray,
8
- isEmpty,
9
- isNaN,
10
- toNumber,
11
- } from 'lodash';
1
+ import get from 'lodash/get';
2
+ import isBoolean from 'lodash/isBoolean';
3
+ import isNumber from 'lodash/isNumber';
4
+ import isNull from 'lodash/isNull';
5
+ import isObject from 'lodash/isObject';
6
+ import isEmpty from 'lodash/isEmpty';
7
+ import isNaN from 'lodash/isNaN';
8
+ import toNumber from 'lodash/toNumber';
12
9
 
13
10
  import * as yup from 'yup';
14
11
  import { translatedErrors as errorsTrads } from '@strapi/helper-plugin';
15
12
 
13
+ import isValidJSONString from './isValidJSONString';
14
+
16
15
  yup.addMethod(yup.mixed, 'defined', function() {
17
16
  return this.test('defined', errorsTrads.required, value => value !== undefined);
18
17
  });
@@ -224,7 +223,7 @@ const createYupSchemaAttribute = (type, validations, options) => {
224
223
  return true;
225
224
  }
226
225
 
227
- if (isNumber(value) || isNull(value) || isObject(value) || isArray(value)) {
226
+ if (isValidJSONString(value) || isNumber(value) || isNull(value) || isObject(value)) {
228
227
  return true;
229
228
  }
230
229
 
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { Field } from '@strapi/design-system/Field';
5
+
6
+ const FieldWrapper = ({ name, hint, error, children }) => {
7
+ const { formatMessage } = useIntl();
8
+ const errorMessage = error ? formatMessage({ id: error, defaultMessage: error }) : '';
9
+
10
+ return (
11
+ <Field name={name} hint={hint && formatMessage(hint)} error={errorMessage} id={name}>
12
+ {children}
13
+ </Field>
14
+ );
15
+ };
16
+
17
+ FieldWrapper.defaultProps = {
18
+ hint: undefined,
19
+ error: '',
20
+ };
21
+
22
+ FieldWrapper.propTypes = {
23
+ name: PropTypes.string.isRequired,
24
+ hint: PropTypes.shape({
25
+ id: PropTypes.string,
26
+ defaultMessage: PropTypes.string,
27
+ }),
28
+ error: PropTypes.string,
29
+ children: PropTypes.node.isRequired,
30
+ };
31
+
32
+ export default FieldWrapper;
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import styled from 'styled-components';
4
4
  import { useIntl } from 'react-intl';
5
- import { Typography } from '@strapi/design-system/Typography';
5
+ import { FieldLabel } from '@strapi/design-system/Field';
6
6
  import { Box } from '@strapi/design-system/Box';
7
7
  import { Flex } from '@strapi/design-system/Flex';
8
8
 
@@ -12,7 +12,7 @@ const LabelAction = styled(Box)`
12
12
  }
13
13
  `;
14
14
 
15
- const Label = ({ id, intlLabel, labelAction, name }) => {
15
+ const Label = ({ intlLabel, labelAction, name, required }) => {
16
16
  const { formatMessage } = useIntl();
17
17
  const label = intlLabel?.id
18
18
  ? formatMessage(
@@ -23,15 +23,7 @@ const Label = ({ id, intlLabel, labelAction, name }) => {
23
23
 
24
24
  return (
25
25
  <Flex>
26
- <Typography
27
- textColor="neutral800"
28
- htmlFor={id || name}
29
- variant="pi"
30
- fontWeight="bold"
31
- as="label"
32
- >
33
- {label}
34
- </Typography>
26
+ <FieldLabel required={required}>{label}</FieldLabel>
35
27
  {labelAction && <LabelAction paddingLeft={1}>{labelAction}</LabelAction>}
36
28
  </Flex>
37
29
  );
@@ -41,6 +33,7 @@ Label.defaultProps = {
41
33
  id: undefined,
42
34
  intlLabel: undefined,
43
35
  labelAction: undefined,
36
+ required: false,
44
37
  };
45
38
 
46
39
  Label.propTypes = {
@@ -52,6 +45,7 @@ Label.propTypes = {
52
45
  }),
53
46
  labelAction: PropTypes.element,
54
47
  name: PropTypes.string.isRequired,
48
+ required: PropTypes.bool,
55
49
  };
56
50
 
57
51
  export default Label;
@@ -9,11 +9,11 @@ import PropTypes from 'prop-types';
9
9
  import cm from 'codemirror';
10
10
  import trimStart from 'lodash/trimStart';
11
11
  import { Stack } from '@strapi/design-system/Stack';
12
+ import { FieldHint, FieldError } from '@strapi/design-system/Field';
12
13
  import jsonlint from './jsonlint';
13
14
  import { EditorWrapper, StyledBox } from './components';
14
- import Hint from '../Hint';
15
15
  import Label from './Label';
16
- import FieldError from './FieldError';
16
+ import FieldWrapper from './FieldWrapper';
17
17
 
18
18
  const WAIT = 600;
19
19
  const stringify = JSON.stringify;
@@ -80,7 +80,7 @@ class InputJSON extends React.Component {
80
80
  try {
81
81
  if (value === null) return this.codeMirror.setValue('');
82
82
 
83
- const nextValue = typeof value !== 'string' ? stringify(value, null, 2) : value;
83
+ const nextValue = stringify(value, null, 2);
84
84
 
85
85
  return this.codeMirror.setValue(nextValue);
86
86
  } catch (err) {
@@ -159,30 +159,28 @@ class InputJSON extends React.Component {
159
159
  }
160
160
 
161
161
  return (
162
- <Stack size={1}>
163
- <Label
164
- intlLabel={this.props.intlLabel}
165
- labelAction={this.props.labelAction}
166
- name={this.props.name}
167
- />
168
- <StyledBox error={this.props.error}>
169
- <EditorWrapper disabled={this.props.disabled}>
170
- <textarea
171
- ref={this.editor}
172
- autoComplete="off"
173
- id={this.props.id || this.props.name}
174
- defaultValue=""
175
- />
176
- </EditorWrapper>
177
- </StyledBox>
178
- <Hint
179
- description={this.props.description}
180
- name={this.props.name}
181
- id={this.props.id}
182
- error={this.props.error}
183
- />
184
- <FieldError id={this.props.id} name={this.props.name} error={this.props.error} />
185
- </Stack>
162
+ <FieldWrapper name={this.props.name} hint={this.props.description} error={this.props.error}>
163
+ <Stack size={1}>
164
+ <Label
165
+ intlLabel={this.props.intlLabel}
166
+ labelAction={this.props.labelAction}
167
+ name={this.props.name}
168
+ required={this.props.required}
169
+ />
170
+ <StyledBox error={this.props.error}>
171
+ <EditorWrapper disabled={this.props.disabled}>
172
+ <textarea
173
+ ref={this.editor}
174
+ autoComplete="off"
175
+ id={this.props.id || this.props.name}
176
+ defaultValue=""
177
+ />
178
+ </EditorWrapper>
179
+ </StyledBox>
180
+ <FieldHint />
181
+ <FieldError />
182
+ </Stack>
183
+ </FieldWrapper>
186
184
  );
187
185
  }
188
186
  }
@@ -196,6 +194,7 @@ InputJSON.defaultProps = {
196
194
  labelAction: undefined,
197
195
  onChange: () => {},
198
196
  value: null,
197
+ required: false,
199
198
  };
200
199
 
201
200
  InputJSON.propTypes = {
@@ -216,6 +215,7 @@ InputJSON.propTypes = {
216
215
  name: PropTypes.string.isRequired,
217
216
  onChange: PropTypes.func,
218
217
  value: PropTypes.any,
218
+ required: PropTypes.bool,
219
219
  };
220
220
 
221
221
  export default InputJSON;
@@ -23,6 +23,10 @@ const Wrapper = styled.div`
23
23
  margin-block-end: 10px;
24
24
  }
25
25
 
26
+ p {
27
+ margin-bottom: 10px;
28
+ }
29
+
26
30
  h1 {
27
31
  font-size: ${36 / 16}rem;
28
32
  font-weight: 600;
@@ -20,7 +20,7 @@ loadCss();
20
20
  const md = new Markdown({
21
21
  html: true, // Enable HTML tags in source
22
22
  xhtmlOut: false,
23
- breaks: false,
23
+ breaks: true,
24
24
  langPrefix: 'language-',
25
25
  linkify: true,
26
26
  typographer: true,
@@ -121,6 +121,10 @@ const DraggedItem = ({
121
121
  if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
122
122
  return;
123
123
  }
124
+ // If They are not in the same level, should not move
125
+ if (dragPath.split('.').length !== hoverPath.split('.').length) {
126
+ return;
127
+ }
124
128
  // Time to actually perform the action in the data
125
129
  moveComponentField(pathToComponentArray, dragIndex, hoverIndex);
126
130
 
@@ -12,6 +12,7 @@ const Editor = ({
12
12
  editorRef,
13
13
  error,
14
14
  isPreviewMode,
15
+ isExpandMode,
15
16
  name,
16
17
  onChange,
17
18
  placeholder,
@@ -64,7 +65,7 @@ const Editor = ({
64
65
 
65
66
  return (
66
67
  <EditorAndPreviewWrapper>
67
- <EditorStylesContainer disabled={disabled || isPreviewMode}>
68
+ <EditorStylesContainer isExpandMode={isExpandMode} disabled={disabled || isPreviewMode}>
68
69
  <textarea ref={textareaRef} />
69
70
  </EditorStylesContainer>
70
71
  {isPreviewMode && <PreviewWysiwyg data={value} />}
@@ -76,6 +77,7 @@ Editor.defaultProps = {
76
77
  disabled: false,
77
78
  error: undefined,
78
79
  isPreviewMode: false,
80
+ isExpandMode: false,
79
81
  placeholder: '',
80
82
  value: '',
81
83
  };
@@ -85,6 +87,7 @@ Editor.propTypes = {
85
87
  editorRef: PropTypes.shape({ current: PropTypes.any }).isRequired,
86
88
  error: PropTypes.string,
87
89
  isPreviewMode: PropTypes.bool,
90
+ isExpandMode: PropTypes.bool,
88
91
  name: PropTypes.string.isRequired,
89
92
  onChange: PropTypes.func.isRequired,
90
93
  placeholder: PropTypes.string,
@@ -4,6 +4,7 @@ import styled from 'styled-components';
4
4
  /* stylelint-disable */
5
5
  export const EditorStylesContainer = styled.div`
6
6
  cursor: ${({ disabled }) => (disabled ? 'not-allowed !important' : 'auto')};
7
+ height: 100%;
7
8
  /* BASICS */
8
9
  .CodeMirror-placeholder {
9
10
  color: ${({ theme }) => theme.colors.neutral600} !important;
@@ -12,7 +13,7 @@ export const EditorStylesContainer = styled.div`
12
13
  .CodeMirror {
13
14
  /* Set height, width, borders, and global font properties here */
14
15
  font-size: ${14 / 16}rem;
15
- height: 290px;
16
+ height: ${({ isExpandMode }) => (isExpandMode ? '100%' : '290px')};
16
17
  color: ${({ theme }) => theme.colors.neutral800};
17
18
  direction: ltr;
18
19
  font-family: --apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
@@ -28,7 +28,9 @@ import {
28
28
  } from './WysiwygStyles';
29
29
 
30
30
  const WysiwygNav = ({
31
+ disabled,
31
32
  editorRef,
33
+ isExpandMode,
32
34
  isPreviewMode,
33
35
  onActionClick,
34
36
  onToggleMediaLib,
@@ -46,7 +48,7 @@ const WysiwygNav = ({
46
48
  setVisiblePopover(prev => !prev);
47
49
  };
48
50
 
49
- if (isPreviewMode) {
51
+ if (disabled || isPreviewMode) {
50
52
  return (
51
53
  <Box padding={2} background="neutral100">
52
54
  <Flex justifyContent="space-between">
@@ -87,12 +89,14 @@ const WysiwygNav = ({
87
89
  <MoreButton disabled id="more" label="More" icon={<More />} />
88
90
  </Flex>
89
91
 
90
- <Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
91
- {formatMessage({
92
- id: 'components.Wysiwyg.ToggleMode.markdown-mode',
93
- defaultMessage: 'Markdown mode',
94
- })}
95
- </Button>
92
+ {!isExpandMode && (
93
+ <Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
94
+ {formatMessage({
95
+ id: 'components.Wysiwyg.ToggleMode.markdown-mode',
96
+ defaultMessage: 'Markdown mode',
97
+ })}
98
+ </Button>
99
+ )}
96
100
  </Flex>
97
101
  </Box>
98
102
  );
@@ -149,7 +153,7 @@ const WysiwygNav = ({
149
153
  />
150
154
  {visiblePopover && (
151
155
  <Popover centered source={buttonMoreRef} spacing={4} id="popover">
152
- <FocusTrap onEscape={handleTogglePopover}>
156
+ <FocusTrap onEscape={handleTogglePopover} restoreFocus={false}>
153
157
  <Flex>
154
158
  <IconButtonGroupMargin>
155
159
  <CustomIconButton
@@ -235,7 +239,9 @@ WysiwygNav.defaultProps = {
235
239
  };
236
240
 
237
241
  WysiwygNav.propTypes = {
242
+ disabled: PropTypes.bool.isRequired,
238
243
  editorRef: PropTypes.shape({ current: PropTypes.any }).isRequired,
244
+ isExpandMode: PropTypes.bool.isRequired,
239
245
  isPreviewMode: PropTypes.bool,
240
246
  onActionClick: PropTypes.func,
241
247
  onToggleMediaLib: PropTypes.func,
@@ -45,6 +45,7 @@ export const IconButtonGroupMargin = styled(IconButtonGroup)`
45
45
 
46
46
  export const EditorAndPreviewWrapper = styled.div`
47
47
  position: relative;
48
+ height: calc(100% - 48px);
48
49
  `;
49
50
 
50
51
  // FOOTER
@@ -144,15 +144,18 @@ const Wysiwyg = ({
144
144
  onCollapse={handleToggleExpand}
145
145
  >
146
146
  <WysiwygNav
147
+ isExpandMode={isExpandMode}
147
148
  editorRef={editorRef}
148
149
  isPreviewMode={isPreviewMode}
149
150
  onActionClick={handleActionClick}
150
151
  onToggleMediaLib={handleToggleMediaLib}
151
152
  onTogglePreviewMode={isExpandMode ? undefined : handleTogglePreviewMode}
153
+ disabled={disabled}
152
154
  />
153
155
 
154
156
  <Editor
155
157
  disabled={disabled}
158
+ isExpandMode={isExpandMode}
156
159
  editorRef={editorRef}
157
160
  error={errorMessage}
158
161
  isPreviewMode={isPreviewMode}
@@ -185,7 +188,7 @@ const Wysiwyg = ({
185
188
 
186
189
  Wysiwyg.defaultProps = {
187
190
  description: null,
188
- disabled: true,
191
+ disabled: false,
189
192
  error: '',
190
193
  labelAction: undefined,
191
194
  placeholder: null,
@@ -41,9 +41,7 @@ const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField,
41
41
  <Box padding={4} hasRadius borderStyle="dashed" borderWidth="1px" borderColor="neutral300">
42
42
  <Stack size={2}>
43
43
  {editLayout.map((row, index) => (
44
- <React.Fragment key={row.rowId}>
45
- <RowsLayout row={row} rowIndex={index} onRemoveField={onRemoveField} />
46
- </React.Fragment>
44
+ <RowsLayout key={row.rowId} row={row} rowIndex={index} onRemoveField={onRemoveField} />
47
45
  ))}
48
46
  <SimpleMenu
49
47
  id="label"
@@ -26,7 +26,7 @@ const HeaderContainer = styled(Flex)`
26
26
  }
27
27
  `;
28
28
 
29
- const FormModal = ({ onToggle, onChange, onSubmit, type }) => {
29
+ const FormModal = ({ onToggle, onMetaChange, onSizeChange, onSubmit, type }) => {
30
30
  const { selectedField } = useLayoutDnd();
31
31
  const { formatMessage } = useIntl();
32
32
 
@@ -61,7 +61,7 @@ const FormModal = ({ onToggle, onChange, onSubmit, type }) => {
61
61
  </ModalHeader>
62
62
  <ModalBody>
63
63
  <Grid gap={4}>
64
- <ModalForm onChange={onChange} />
64
+ <ModalForm onMetaChange={onMetaChange} onSizeChange={onSizeChange} />
65
65
  </Grid>
66
66
  </ModalBody>
67
67
  <ModalFooter
@@ -84,7 +84,8 @@ const FormModal = ({ onToggle, onChange, onSubmit, type }) => {
84
84
  FormModal.propTypes = {
85
85
  onSubmit: PropTypes.func.isRequired,
86
86
  onToggle: PropTypes.func.isRequired,
87
- onChange: PropTypes.func.isRequired,
87
+ onMetaChange: PropTypes.func.isRequired,
88
+ onSizeChange: PropTypes.func.isRequired,
88
89
  type: PropTypes.string.isRequired,
89
90
  };
90
91
 
@@ -1,6 +1,8 @@
1
1
  import React, { useMemo, useCallback } from 'react';
2
+ import PropTypes from 'prop-types';
2
3
  import get from 'lodash/get';
3
4
  import { GridItem } from '@strapi/design-system/Grid';
5
+ import { Select, Option } from '@strapi/design-system/Select';
4
6
  import { useSelector, shallowEqual } from 'react-redux';
5
7
  import { useIntl } from 'react-intl';
6
8
  import { useLayoutDnd } from '../../../hooks';
@@ -9,7 +11,11 @@ import { makeSelectModelAndComponentSchemas } from '../../App/selectors';
9
11
  import getTrad from '../../../utils/getTrad';
10
12
  import GenericInput from './GenericInput';
11
13
 
12
- const ModalForm = ({ onChange }) => {
14
+ const FIELD_SIZES = [[4, '33%'], [6, '50%'], [8, '66%'], [12, '100%']];
15
+
16
+ const NON_RESIZABLE_FIELD_TYPES = ['dynamiczone', 'component', 'json', 'richtext'];
17
+
18
+ const ModalForm = ({ onMetaChange, onSizeChange }) => {
13
19
  const { formatMessage } = useIntl();
14
20
  const { modifiedData, selectedField, attributes, fieldForm } = useLayoutDnd();
15
21
  const schemasSelector = useMemo(makeSelectModelAndComponentSchemas, []);
@@ -44,22 +50,21 @@ const ModalForm = ({ onChange }) => {
44
50
  [selectedField, componentsAndModelsPossibleMainFields, modifiedData]
45
51
  );
46
52
 
47
- return formToDisplay.map(meta => {
53
+ const metaFields = formToDisplay.map(meta => {
48
54
  const formType = get(attributes, [selectedField, 'type']);
49
55
 
50
- if (formType === 'dynamiczone' && !['label', 'description'].includes(meta)) {
51
- return null;
52
- }
53
-
54
- if ((formType === 'component' || formType === 'media') && meta !== 'label') {
56
+ if (
57
+ formType === 'dynamiczone' ||
58
+ (formType === 'component' && !['label', 'description'].includes(meta))
59
+ ) {
55
60
  return null;
56
61
  }
57
62
 
58
- if ((formType === 'json' || formType === 'boolean') && meta === 'placeholder') {
63
+ if (formType === 'component' && meta !== 'label') {
59
64
  return null;
60
65
  }
61
66
 
62
- if (formType === 'richtext' && meta === 'editable') {
67
+ if (['media', 'json', 'boolean'].includes(formType) && meta === 'placeholder') {
63
68
  return null;
64
69
  }
65
70
 
@@ -78,13 +83,49 @@ const ModalForm = ({ onChange }) => {
78
83
  id: get(getInputProps(meta), 'label.id', 'app.utils.defaultMessage'),
79
84
  })}
80
85
  name={meta}
81
- onChange={onChange}
82
- value={get(fieldForm, meta, '')}
86
+ onChange={onMetaChange}
87
+ value={get(fieldForm, ['metadata', meta], '')}
83
88
  options={getSelectedItemSelectOptions(formType)}
84
89
  />
85
90
  </GridItem>
86
91
  );
87
92
  });
93
+
94
+ const canResize = !NON_RESIZABLE_FIELD_TYPES.includes(attributes[selectedField].type);
95
+
96
+ const sizeField = (
97
+ <GridItem col={6} key="size">
98
+ <Select
99
+ value={fieldForm?.size}
100
+ name="size"
101
+ onChange={value => {
102
+ onSizeChange({ name: selectedField, value });
103
+ }}
104
+ label={formatMessage({
105
+ id: getTrad('containers.SettingPage.editSettings.size.label'),
106
+ defaultMessage: 'Size',
107
+ })}
108
+ >
109
+ {FIELD_SIZES.map(([value, label]) => (
110
+ <Option key={value} value={value}>
111
+ {label}
112
+ </Option>
113
+ ))}
114
+ </Select>
115
+ </GridItem>
116
+ );
117
+
118
+ return (
119
+ <>
120
+ {metaFields}
121
+ {canResize && sizeField}
122
+ </>
123
+ );
124
+ };
125
+
126
+ ModalForm.propTypes = {
127
+ onMetaChange: PropTypes.func.isRequired,
128
+ onSizeChange: PropTypes.func.isRequired,
88
129
  };
89
130
 
90
131
  export default ModalForm;
@@ -110,6 +110,14 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, upd
110
110
  });
111
111
  };
112
112
 
113
+ const handleSizeChange = ({ name, value }) => {
114
+ dispatch({
115
+ type: 'ON_CHANGE_SIZE',
116
+ name,
117
+ value,
118
+ });
119
+ };
120
+
113
121
  const handleMetaSubmit = e => {
114
122
  e.preventDefault();
115
123
  dispatch({
@@ -365,7 +373,8 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, upd
365
373
  onSubmit={handleMetaSubmit}
366
374
  onToggle={handleToggleModal}
367
375
  type={get(attributes, [metaToEdit, 'type'], '')}
368
- onChange={handleMetaChange}
376
+ onMetaChange={handleMetaChange}
377
+ onSizeChange={handleSizeChange}
369
378
  />
370
379
  )}
371
380
  </Main>
@@ -4,7 +4,7 @@ import get from 'lodash/get';
4
4
  import cloneDeep from 'lodash/cloneDeep';
5
5
 
6
6
  import { arrayMoveItem } from '../../utils';
7
- import { formatLayout, getInputSize } from './utils/layout';
7
+ import { formatLayout, getDefaultInputSize, getFieldSize, setFieldSize } from './utils/layout';
8
8
 
9
9
  const initialState = {
10
10
  fieldForm: {},
@@ -45,7 +45,7 @@ const reducer = (state = initialState, action) =>
45
45
  }
46
46
  case 'ON_ADD_FIELD': {
47
47
  const newState = cloneDeep(state);
48
- const size = getInputSize(
48
+ const size = getDefaultInputSize(
49
49
  get(newState, ['modifiedData', 'attributes', action.name, 'type'], '')
50
50
  );
51
51
  const listSize = get(newState, layoutPathEdit, []).length;
@@ -76,7 +76,11 @@ const reducer = (state = initialState, action) =>
76
76
  break;
77
77
  }
78
78
  case 'ON_CHANGE_META': {
79
- set(draftState, ['metaForm', ...action.keys], action.value);
79
+ set(draftState, ['metaForm', 'metadata', ...action.keys], action.value);
80
+ break;
81
+ }
82
+ case 'ON_CHANGE_SIZE': {
83
+ set(draftState, ['metaForm', 'size'], action.value);
80
84
  break;
81
85
  }
82
86
  case 'ON_RESET': {
@@ -168,11 +172,28 @@ const reducer = (state = initialState, action) =>
168
172
  }
169
173
  case 'SET_FIELD_TO_EDIT': {
170
174
  draftState.metaToEdit = action.name;
171
- draftState.metaForm = get(state, ['modifiedData', 'metadatas', action.name, 'edit'], {});
175
+ draftState.metaForm = {
176
+ metadata: get(state, ['modifiedData', 'metadatas', action.name, 'edit'], {}),
177
+ size:
178
+ getFieldSize(action.name, state.modifiedData?.layouts?.edit) ?? getDefaultInputSize(),
179
+ };
180
+
172
181
  break;
173
182
  }
174
183
  case 'SUBMIT_META_FORM': {
175
- set(draftState, ['modifiedData', 'metadatas', state.metaToEdit, 'edit'], state.metaForm);
184
+ set(
185
+ draftState,
186
+ ['modifiedData', 'metadatas', state.metaToEdit, 'edit'],
187
+ state.metaForm.metadata
188
+ );
189
+
190
+ const layoutsCopy = cloneDeep(get(state, layoutPathEdit, []));
191
+ const nextLayoutValue = setFieldSize(state.metaToEdit, state.metaForm.size, layoutsCopy);
192
+
193
+ if (nextLayoutValue.length > 0) {
194
+ set(draftState, layoutPathEdit, formatLayout(nextLayoutValue));
195
+ }
196
+
176
197
  break;
177
198
  }
178
199
  case 'SUBMIT_SUCCEEDED': {